// LavalampProvider.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include <assert.h>
#include <process.h>
#include <winsock2.h>
#include "LavaParser.h"
#include "LavalampProvider.h"
#include "../TracerDLL.h"
#include "../urlparse/urlparse.h"

#define TIME_OUT 6000 // in milliseconds

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    return TRUE;
}


INewsContentProvider* CreateProvider()
{
	INewsContentProvider* p = new CLavalampProvider();
   DBG_NewMem(p, "INewsContentProvider created.");
	return p;
}

void ReleaseProvider(INewsContentProvider* pvdr)
{
	if (pvdr) {
		delete (CLavalampProvider*)pvdr;
      DBG_DelMem(pvdr, "INewsContentProvider deleted.");
	}
}

void StartUpdate (void* owner);
int GetConfiguration(const char* szConfigFile, CURLParser* parser);

#define NO_CONTENT "No information available..."
#define ALL_PASS "All PASS!"
#define URL "http://10.144.28.29:8080/composite-lavalamp/composite_lavalamp_ipa_k.jsp"
#define FAILED_TXT " FAILED "
#define OK_TXT " OK "

#define CONFIG_FILENAME "lava.conf"

CLavalampProvider::CLavalampProvider()
{
   m_hMutex = CreateMutex(NULL, FALSE, NULL);
   m_nCount = 0;
   SetNoContent();
   m_nClock = DEFAULT_UPDATE_INTERVAL;
   m_nUpdateInterval = DEFAULT_UPDATE_INTERVAL;
   m_bShowSuccesses = false;
   GetConfiguration (CONFIG_FILENAME, &m_parser);
   m_uThreadID = _beginthread(StartUpdate, 0, this);
}

CLavalampProvider::~CLavalampProvider()
{
   WaitForSingleObject(m_hMutex, INFINITE);
   FreeItems();
   ReleaseMutex(m_hMutex);
   CloseHandle(m_hMutex);
}

void CLavalampProvider::GetNextNewsItem(NewsItem** ppItem)
{
   WaitForSingleObject(m_hMutex, INFINITE);
	if (m_nIdx == m_nCount) {
		*ppItem = NULL;
		m_nIdx = 0;
      ReleaseMutex(m_hMutex);
		return;
	}
	
	assert (m_nIdx < m_nCount);
	*ppItem = &(m_items[m_nIdx++]);
   ReleaseMutex(m_hMutex);
	return;
}

void CLavalampProvider::Tick()
{
   WaitForSingleObject(m_hMutex, INFINITE);
	m_nClock -= (TICK_INTERVAL)/1000;
	if (m_nClock <= 0 && m_uThreadID == INVALID_THREAD) {
		m_uThreadID = _beginthread(StartUpdate, 0, this);
		m_nClock = m_nUpdateInterval;
	}
   ReleaseMutex(m_hMutex);
}

size_t CLavalampProvider::GetURLSize()
{
	return (strlen(m_parser.m_url)+1);
}

int CLavalampProvider::GetURL(char* url_buf, size_t bufsize)
{
	if (bufsize >= strlen(m_parser.m_url)) {
		strcpy (url_buf, m_parser.m_url);
		return 0;
	}
	return -1;
}

void CLavalampProvider::WebFetched(char* szFilename)
{
   CLavalampParser psFile;
   int nRet = 0;

   WaitForSingleObject(m_hMutex, INFINITE);

   m_uThreadID = INVALID_THREAD;

   nRet = psFile.OpenFile (szFilename);
   if (nRet != 0) {
      ReleaseMutex(m_hMutex);
      return;
   }
   
   FreeItems();   
   bool bFoundOkItems = false;
   int i = 0;   
   do {
      nRet = psFile.GetNextLavalampInfoLength();
      if (nRet >= 0) {
         char* buf = (char*) malloc (nRet + 1);
         DBG_NewMem (buf, "WebFetched. buffer to store the information.");
         nRet = psFile.CopyLavalampInfo (buf, nRet + 1);
  
         if (!m_bShowSuccesses && strstr(buf, OK_TXT)) {
            bFoundOkItems = true;
            free(buf);
            DBG_DelMem (buf, "WebFetched. Buffer to store information, freed.");
         } else {
			   AddItem(buf, i);
			   i++;
		   } 
      }
   } while (nRet >= 0);
   
   if (i == 0 ) {
      if (bFoundOkItems) {
	      SetAllPass();
      } else {
         SetNoContent();
      }
   }

   psFile.CloseFile ();
   if (m_nIdx >= m_nCount) {
	   m_nIdx = 0;
   }

   ReleaseMutex(m_hMutex);
   return;	
}

void CLavalampProvider::SetUpdateInterval (int nMinutes)
{
	if (nMinutes <= 0) {
		m_nUpdateInterval = DEFAULT_UPDATE_INTERVAL * 60;
	} else {
		m_nUpdateInterval = nMinutes * 60;
	}
}

void CLavalampProvider::ShowAllSuccessfulItems(bool bShow)
{
   WaitForSingleObject(m_hMutex, INFINITE);
	m_bShowSuccesses = bShow;
   ReleaseMutex(m_hMutex);
}


void CLavalampProvider::SetNoContent()
{
    FreeItems();
	m_items[0].bkgrd = RGB(255, 255 ,0);
	m_items[0].fore = RGB (0, 0, 255);
	m_items[0].text = (char*) malloc (sizeof(NO_CONTENT));
    DBG_NewMem (m_items[0].text, "SetNoContent, text");
	strcpy (m_items[0].text, NO_CONTENT);
	m_nCount = 1;
	m_nIdx = 0;
}

void CLavalampProvider::SetAllPass()
{
    FreeItems();
	m_items[0].bkgrd = RGB(0, 255 ,0);
	m_items[0].fore = RGB (0, 0, 0);
	m_items[0].text = (char*) malloc (sizeof(ALL_PASS));
    DBG_NewMem (m_items[0].text, "SetAllPass, text");
	strcpy (m_items[0].text, ALL_PASS);
	m_nCount = 1;
	m_nIdx = 0;
}

void CLavalampProvider::FreeItems()
{
	int i;
	
	assert (m_nCount <= MAX_CCIS && m_nCount >= 0);
	for (i = 0; i < m_nCount; i ++) {
		if (m_items[i].text ) {
			free (m_items[i].text);
         DBG_DelMem (m_items[i].text, "Free Item, text freed.");
			m_items[i].text = NULL;
		}
	}
	m_nCount = 0;
	m_nIdx = 0;
}

void CLavalampProvider::AddItem (char* txt, int nIdx)
{
	m_items[nIdx].text = txt;
	if (strstr(txt, FAILED_TXT)) {
		m_items[nIdx].bkgrd = RGB(255, 0, 0);
		m_items[nIdx].fore = RGB(255, 255, 255);
	} else if (strstr(txt, OK_TXT)) {
		m_items[nIdx].bkgrd = RGB(0, 255, 0);
		m_items[nIdx].fore = RGB(0, 0, 0);
	} else {
		m_items[nIdx].bkgrd = RGB(255, 255, 0);
		m_items[nIdx].fore = RGB(0, 0, 255);
	}
	m_nCount = nIdx + 1;
}

#define GET_COMMAND "GET "
#define NEW_LINE "\r\n"
#define FILENAME  "lava.xml"
#define BUFSIZE 1024

void StartUpdate (void* owner)
{
	struct hostent* pHostent;
	FILE* f;	
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	SOCKET so;
	int i = 0;
    int to = TIME_OUT;
	CLavalampProvider* pOwner = (CLavalampProvider*)owner;
    unsigned int bytes_sent;

	assert (pOwner);

	f = fopen(FILENAME, "w");
	if (!f) {
		goto err0;
	}
	
	wVersionRequested = MAKEWORD( 2, 2 );
 
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		/* Tell the user that we could not find a usable */
		/* WinSock DLL.                                  */
		goto err0;
	}

	pHostent = gethostbyname (pOwner->m_parser.m_domain);
	if (!pHostent) {
		goto err1;
	}

	if (!pHostent->h_addr_list[0]) {
		goto err1;
	}

	so = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in si;
	si.sin_family = AF_INET;
	si.sin_port = ntohs(pOwner->m_parser.m_port);
	memcpy (&si.sin_addr, pHostent->h_addr_list[0], pHostent->h_length);

	err = connect (so, (struct sockaddr*)&si, sizeof(si));

	if (err) {
		goto err2;
	}
	
   err = setsockopt (so, SOL_SOCKET, SO_SNDTIMEO, (const char*)&to, sizeof (int));
   if (err) {
      goto err2;
   }
   err = setsockopt (so, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof (int));
   if (err) {
      goto err2;
   }


    bytes_sent = send(so, GET_COMMAND, strlen(GET_COMMAND), 0); 
    if (bytes_sent != strlen (GET_COMMAND)) {
      // Not necessary a fault. However, since the traffic is not big,
      // and GET_COMMAND length is short. This should be sent in one send call.
      //  So, here we assume there is a fault.
      goto err2;
    }
    bytes_sent = send (so, pOwner->m_parser.m_path, strlen (pOwner->m_parser.m_path), 0);
    if (bytes_sent != strlen (pOwner->m_parser.m_path)) {
      // Not necessary a fault. However, since the traffic is not big,
      // and GET_COMMAND length is short. This should be sent in one send call.
      //  So, here we assume there is a fault.
      goto err2;
    }
    bytes_sent = send (so, NEW_LINE, strlen (NEW_LINE), 0);
    if (bytes_sent != strlen (NEW_LINE)) {
      // Not necessary a fault. However, since the traffic is not big,
      // and GET_COMMAND length is short. This should be sent in one send call.
      //  So, here we assume there is a fault.
      goto err2;
    }

	char buf[BUFSIZE];
	
	int bytes;
	do {
		memset (buf, 0, BUFSIZE);
		bytes = recv(so, buf, BUFSIZE, 0);
		if (bytes > 0 && bytes <=BUFSIZE) {
			fwrite (buf, 1, bytes, f);
		}
	} while (bytes > 0  && bytes <=BUFSIZE);


err2:
	closesocket(so);
err1:
	WSACleanup();
err0:
	fclose (f);
	pOwner->WebFetched(FILENAME);
	return;
}

#define CONFIG_HEADER_URL   "URL:"

int GetConfiguration(const char* szConfigFile, CURLParser* parser)
{
    assert (szConfigFile);
    FILE* f = fopen (szConfigFile, "r");
    if (!f) {
        return -1;
    }
    
    char line[BUFSIZE];

    while (fgets(line, BUFSIZE, f)) {
        const char* txt = GetFirstNonSpace(line);
        if (strncmp (txt, CONFIG_HEADER_URL, strlen (CONFIG_HEADER_URL)) == 0) {
            parser->Parse(txt + strlen(CONFIG_HEADER_URL));
        }
    }
    fclose (f);
    return 0;
}

